import {
    proxy_GetPagePropertyList,
    proxy_AddPageProperty,
    proxy_UpdatePageProperty,
    proxy_DeletePageProperty,
    proxy_GetPageContent,
    proxy_AddWidgetContent,
    proxy_UpdateWidgetContent,
    proxy_MoveWidgetContent,
    proxy_DeleteWidgetContent,
} from './server-proxy.js';

import {EventDispatcher} from "./framework.js"
//import {ToastMessager, MessagerLevel} from './messager.js'
import {Utils, TypeCheck, _EMPTY_FN_} from './common.js'
import {TreeNode, Tree} from "./tree.js"
import Collection from "./collection.js"

const _F = Utils.F;
const _DEP = Utils.DefineEnumProperty; 
const _GUID = Utils.GUID;

class PageConfigNode extends TreeNode {
    constructor(data) {
        super();
        this._data = data;
    }
    get data() {return this._data;}
    get SUID() {return this._data.SUID;}
}

function IsSameMetaData(A, B) {
    if(B.SUID != A.SUID) return false;
    if(B.name != A.name) return false;
    if(B.createTime.toISOString() != A.createTime.toISOString()) return false;
    if(B.modifyTime.toISOString() != A.modifyTime.toISOString()) return false;
    if(B.published != A.published) return false;
    if(B.tags.toString() != A.tags.toString()) return false;
    if(B.brief != A.brief) return false;
    if(B.toc != A.toc) return false;
    if(B.PSUID != A.PSUID) return false;
    return true;
}

function CopyFromBtoA(A, B) {
    A.PSUID = B.PSUID;
    A.name = B.name;
    A.createTime = new Date(B.createTime); //instanceof Date;
    A.modifyTime = new Date(B.modifyTime);
    A.brief = B.brief;
    A.published = B.published;
    A.toc = B.toc;
    A.tags = [];
    Object.assign(A.tags, B.tags);
}

class PageProperty {
    constructor(PID) {
        _DEP(this, 'SUID', PID);
        this.PSUID;
        this.name;
        this.createTime; //instanceof Date;
        this.modifyTime;
        this.brief;
        this.published;
        this.toc;
        this.tags;
    }
}

class WidgetContent {
    constructor(WID, type) {
        _DEP(this, 'SUID', WID);
        _DEP(this, 'type', type);
        this.data = undefined;// can be any type of JS, depend on widget.
    }
}

class PageContent {
    constructor(PID) {
        _DEP(this, 'SUID', PID);
        this.data = Collection('SUID');
    }
    get length() {return this.data.length;}
}

function _Init() {
    _FetchPagePropertyList();
    Init = _EMPTY_FN_;
}

function RegistListener(name, cbFn) {
    _dispatcher.Subscribe(name, cbFn);
}

function _FetchPagePropertyList() {
    proxy_GetPagePropertyList()
        .then(data => {
            _pageConfigHierarchy.RemoveAllChildren(_rootTreeNode);

            const _fn = (data, arr) => {
                arr.push(data);
                for(let i = 0, N = data.children.length; i < N; ++i) {
                    _fn(data.children[i], arr);
                }
            }
            const _arr = [];
            for(let i = 0, N = data.metas.length; i < N; ++i) {
                _fn(data.metas[i], _arr);
            }

            const N = _arr.length;
            for(let i = 0; i < N; ++i) {
                const _jsonMetaData = _arr[i];
                let _meta = new PageProperty(_jsonMetaData.SUID ?? _GUID());
                _meta.PSUID = _jsonMetaData.PSUID;
                _meta.name = _jsonMetaData.name ?? '<no name>';
                _meta.createTime = _jsonMetaData.createTime ? new Date(_jsonMetaData.createTime) : new Date();
                _meta.modifyTime = _jsonMetaData.modifyTime ? new Date(_jsonMetaData.modifyTime) : new Date();
                _meta.published = _jsonMetaData.published ?? false;
                _meta.brief = _jsonMetaData.brief ?? "";
                _meta.toc = _jsonMetaData.toc ?? false;
                _meta.tags = _jsonMetaData.tags ?? [];

                if(!_pageConfigHierarchy.InsertAIntoB(new PageConfigNode(_meta), _rootTreeNode)) {
                    //ToastMessager.Show(`duplicated server PageProperty, slog discarded. SUID: ${_meta.SUID}`, MessagerLevel.WARN);
                }
            }

            for(let i = 0; i < N; ++i) {
                const _jsonMetaData = _arr[i];
                const _node = _pageConfigHierarchy.GetNodeByKey(_jsonMetaData.SUID);
                const _pNode = _pageConfigHierarchy.GetNodeByKey(_jsonMetaData.PSUID) ?? _rootTreeNode;
                _pageConfigHierarchy.MoveAIntoB(_node, _pNode);
            }
            _dispatcher.Dispatch(_F({type: _API.EVT_DATASTORAGE_METAS_FETCHED, pageTree: _pageConfigHierarchy}));
        });
}

function CreateWidgetContent(PID, offset, type) {
    //console.log(PID, offset);
    const _pContent = _arrPageContent.GetByKey(PID);
    if(_pContent) {
        const _wContent = new WidgetContent(_GUID(), type);
        if(_pContent.data.Insert(_wContent, offset)) {
            proxy_AddWidgetContent(PID, offset, _wContent);
            _dispatcher.Dispatch(_F({type: _API.EVT_DATASTORAGE_WIDGET_CONTENT_CREATED, PID, offset, widgetContent: _wContent}));
        }
    }
}

function UpdateWidgetContent(PID, WID) {
    const _pContent = _arrPageContent.GetByKey(PID);
    if(_pContent) {
        const _wContent = _pContent.data.GetByKey(WID);
        if(_wContent) {
            ///_pContent.data.DebugPrint("UpdateWidgetContent");
            proxy_UpdateWidgetContent(PID, _wContent);
            //_dispatcher.Dispatch(_F({type: _API.EVT_DATASTORAGE_SECTION_MODIFIED, PID, widgetContent: _wContent}));
            return true;
        }
    }
    return false;
}
function UpdateWidgetContentIndex(PID, WID, index) {
    let _pContent = _arrPageContent.GetByKey(PID);
    if(_pContent) {
        const _wContent = _pContent.data.GetByKey(WID);
        if(_wContent) {
            _pContent.data.MoveToIndex(_wContent, index);
            proxy_MoveWidgetContent(PID, WID, index);
        }
    }
    return false;
}
function DeleteWidgetContent(PID, WID) {
    const _pContent = _arrPageContent.GetByKey(PID);
    if(_pContent) {
        const _r = _pContent.data.RemoveByKey(WID);
        if(_r) {
            proxy_DeleteWidgetContent(PID, WID);
        }
        return _r;
    }
    return false;
}
function GetWidgetContentIndex(PID, WID) {
    const _pContent = _arrPageContent.GetByKey(PID);
    if(_pContent) {
        return _pContent.data.GetIndexByKey(WID);
    }
    return -1;
}

function FetchPageContent(SUID) {
    const _v = _arrPageContent.GetByKey(SUID);
    if(_v) {
        _dispatcher.Dispatch(_F({type: _API.EVT_DATASTORAGE_ARTICLE_FETCHED, pageContent: _v}));
    } else {
        _RequestArticleData(SUID);
    }
}
function _RequestArticleData(SUID) {
    proxy_GetPageContent(SUID)
        .then(data => {
            let _pContent = new PageContent(SUID);
            if(_arrPageContent.Insert(_pContent)) {
                try {
                    const _data = JSON.parse(data);
                    for(let i = 0, N = _data.indexes.length; i < N; ++i) {
                        const _SUID = _data.indexes[i];
                        const _sectionJsonData = _data.sections[_SUID];
                        let _section = new WidgetContent(_sectionJsonData.SUID ?? _GUID(), _sectionJsonData.type);
                        _section.data = _sectionJsonData.data ?? '';
                        if(!_pContent.data.Insert(_section)) {
                            //ToastMessager.Show(`internal error, check console error for more information.`, MessagerLevel.ERROR);
                            console.error(`internal error: push to unique array.\n comming data:${_sectionJsonData}\nstored data:${_data}`);
                        }
                    }
                } catch(e) {
                }
                _dispatcher.Dispatch(_F({type: _API.EVT_DATASTORAGE_ARTICLE_FETCHED, pageContent: _pContent}));
            } else {
                //ToastMessager.Show(`duplicated server PageContent, slog discarded. SUID: ${SUID}`, MessagerLevel.WARN);
            }

        })
}

function GetPageProperty(SUID) {
    const _node = _pageConfigHierarchy.GetNodeByKey(SUID);
    return _node ? _node.data : null;
}

function UpdatePageProperty(value) {
    let _meta = GetPageProperty(value.SUID);
    if(_meta) {
        if(IsSameMetaData(value, _meta)) return false;
        else {
            CopyFromBtoA(_meta, value);
            proxy_UpdatePageProperty(_meta).then(() => {});
            _dispatcher.Dispatch(_F({type: _API.EVT_DATASTORAGE_META_MODIFIED, pageProperty: _meta}));
            return true;
        }
    } else {
        //ToastMessager.Show(`NO such PageProperty exist to update.\nSUID: ${value.SUID}`, MessagerLevel.ERROR);
        return false;
    }
}

function AddPageProperty(value) {
    let _meta = GetPageProperty(value.SUID);
    if(_meta) {
        //ToastMessager.Show(`duplicated new PageProperty SUID, slog discarded the new one. SUID: ${value.SUID}`, MessagerLevel.WARN);
        return false;
    } else {
        _meta = new PageProperty(value.SUID);
        CopyFromBtoA(_meta, value);
        const _node = new PageConfigNode(_meta);
        let _pNode = _pageConfigHierarchy.GetNodeByKey(value.PSUID) ?? _rootTreeNode;
        if(_pageConfigHierarchy.InsertAIntoB(_node, _pNode)) {
            proxy_AddPageProperty(_meta).then(() => {});
            _dispatcher.Dispatch(_F({type: _API.EVT_DATASTORAGE_META_ADDED, pageProperty: _meta}));
            return true;
        } else {
            //ToastMessager.Show(`internal error, check console error for more information.`, MessagerLevel.ERROR);
            console.error(`internal error: add new MeatData to unique array failed.\n comming meta:${_meta}\nstored metas:${_meta}`);
            return false;
        }
    }
}

function DeletePage(PID) {
    const _node = _pageConfigHierarchy.GetNodeByKey(PID);
    if(_node) {

        let _proxy = _arrPageContent.RemoveByKey(PID);
        if(_proxy) {
            proxy_DeletePageProperty(PID);
            _dispatcher.Dispatch(_F({type: _API.EVT_DATASTORAGE_META_DELETED, PID}));
            return true;
        } else {
            //ToastMessager.Show(`NO Article found, slog discarded the logic.\nSUID: ${PID}`, MessagerLevel.ERROR);
            return false;
        }
    } else {
        //ToastMessager.Show(`NO PageProperty found, slog discarded the logic.\nSUID: ${PID}`, MessagerLevel.ERROR);
        return false;
    }
}

function GetNode(SUID) {
    return _pageConfigHierarchy.GetNodeByKey(SUID);
}

const _dispatcher = EventDispatcher();
const _arrPageContent = Collection('SUID');
const _rootTreeNode = new PageConfigNode({SUID: String(Math.random())});
const _pageConfigHierarchy = new Tree('SUID', _rootTreeNode);



let Init = _Init;
let _API = {
    Init,
    RegistListener,

    PageContent,
    WidgetContent,

    GetPageProperty,
    GetNode,
    UpdatePageProperty,
    AddPageProperty,
    DeletePage,

    FetchPageContent,
    CreateWidgetContent,
    UpdateWidgetContent,
    UpdateWidgetContentIndex,
    DeleteWidgetContent,
    GetWidgetContentIndex,

    EVT_DATASTORAGE_META_MODIFIED: Symbol(),
    EVT_DATASTORAGE_META_DELETED: Symbol(),
    EVT_DATASTORAGE_META_ADDED: Symbol(),
    EVT_DATASTORAGE_METAS_SWITCHED: Symbol(),
    EVT_DATASTORAGE_METAS_FETCHED: Symbol(),
    EVT_DATASTORAGE_SECTION_MODIFIED: Symbol(),
    EVT_DATASTORAGE_SECTION_DELETED: Symbol(),
    EVT_DATASTORAGE_SECTION_ADDED: Symbol(),
    EVT_DATASTORAGE_SECTION_INSERTED: Symbol(),
    EVT_DATASTORAGE_ARTICLE_FETCHED: Symbol(),
    EVT_DATASTORAGE_WIDGET_CONTENT_CREATED: Symbol(),
};


export default Object.freeze(_API);
